#include <user/ulib.h>
#include <user/e100.h>

/* Here are the implementation of INTEL E100 driver */

static struct pci_func func;
static uint16_t port;

struct cb_header_t
{
	 volatile uint16_t status;
	 uint16_t cmd;
	 uint32_t link;
} __attribute__ ((packed));

#define PACKET_SIZE_MAX 1518
#define CB_SIZE         2048
#define CB_COUNT        64

struct control_block_t
{
	 union
	 {
		  struct
		  {
			   struct cb_header_t header;
			   
			   union
			   {
					struct
					{
						 struct
						 {
							  uint32_t _tbd_array_ptr; /* should be -1 */
							  int16_t  size;			 /* signal bit for eof */
							  uint8_t  thd;
							  uint8_t  tbd_num; /* should be 0 */
						 } tp_header __attribute__ ((packed));
						 
						 char packet_data[PACKET_SIZE_MAX];
						 
					} transmit_packet;
			   };
		  };

		  char __size[CB_SIZE];
	 };
};

static char __cb_size1[sizeof(struct control_block_t) == CB_SIZE ? 1 : -1];

int cb_ring_head;
static struct control_block_t *cb_ring;
static char *cb_alloc;

#define CB_CMD_MARK_EL (1 << 15)
#define CB_CMD_MARK_SI (1 << 14)
#define CB_CMD_MARK_I  (1 << 13)

#define CB_CMD_TRANSMIT 4
#define CB_CMD_NOP      0

#define SCB_CMD_CUC_CU_START  (1 << 4)
#define SCB_CMD_CUC_CU_RESUME (2 << 4)

static void
e100_cu_start(int start_idx)
{
	 outl(port + 0x4, va2pa(cb_ring + start_idx));
	 outw(port + 0x2, SCB_CMD_CUC_CU_START);
}

struct mutex_t write_lock;

static int
e100_driver_init(void)
{
	 pipe_clear(rpc_in_pipe, 0);
	 pipe_clear(rpc_out_pipe, 0);

	 rpc_send(get_user_service(SVC_PCI_MANAGER), 0);

	 uint32_t vendor = 0x8086, product = 0x1209;
	 uint32_t method = PCI_ATTACH_BY_VENDOR;

	 pipe_block_write(rpc_in_pipe, &method, sizeof(uint32_t));
	 
	 pipe_block_write(rpc_in_pipe, &vendor, sizeof(uint32_t));
	 pipe_block_write(rpc_in_pipe, &product, sizeof(uint32_t));

	 uint32_t result;

	 pipe_block_read(rpc_out_pipe, &result, sizeof(uint32_t));
	 rpc_wait();
	 
	 if (result != 0)
	 {
		  pipe_block_read(rpc_out_pipe, &func, sizeof(struct pci_func));
	 }
	 else
	 {
		  kprintf("[e100_driver] could not attach the device!\n");
		  return 1;
	 }
	 
	 /* device attached */

	 port = func.reg_base[1];
	 kprintf("[e100_driver] resetting the device (port: %04x) ...", port + 0x08);
	 outl(port + 0x08, 0x0);
	 delay(); delay();

	 kprintf("done\nStart to build control blocks ring\n");

	 cb_alloc = malloc(CB_SIZE * (CB_COUNT + 1));
	 cb_ring = (struct control_block_t *)(((uintptr_t)cb_alloc | (CB_SIZE  -1)) + 1);
	 
	 int i;
	 for (i = 0; i != CB_COUNT; ++i)
	 {
		  cb_ring[i].header.cmd = 0;
		  cb_ring[i].header.link = va2pa(cb_ring + ((i + 1) % CB_COUNT));
	 }

	 cb_ring_head = 0;
	 mutex_init(&write_lock);

	 return 0;
}

void
e100_driver_dispatch(void)
{
	 set_user_service(SVC_E100_DRIVER, self_idx);
	 uint16_t idx;
	 uint32_t num;
	 struct pipe_t *in_pipe, *out_pipe;

	 out_pipe = pipe_open(service_callback);
	 uint32_t zero = 0;
	 pipe_block_write(out_pipe, &zero, sizeof(uint32_t));
	 pipe_close(out_pipe);

	 while (1)
	 {
		  rpc_recv(&idx, &num, &in_pipe, &out_pipe);

		  switch (num)
		  {
		  E100_SEND:
			   kprintf("send request\n");
			   break;
		  E100_RECV:
			   kprintf("recv request\n");
			   break;
		  }
	 }
}

void
e100_driver_entry(void)
{
	 if (e100_driver_init() != 0)
	 {
		  /* TODO */
	 }
	 else e100_driver_dispatch();
}
